(function ()
{
    var DEFAULT_SAMPLE_QUALITY  = 0.3;
    var DEFAULT_QUALITY         = 1;
    var DEFAULT_FILE_PARAM_NAME = "attach";

    var DEFAULT_MAX_WIDTH       = 0;
    var DEFAULT_MAX_HEIGHT      = 0;

    var APPLET_LOAD_TIMEOUT     = 30000; // 30s

    var APPLET_ID               = "ClipboardManagerApplet";

    var ERROR_MESSAGES          = {
        UnsupportedFlavorException : {
            paramName: "unsupported_content",
            defaultMessage: "The content of the clipboard is not supported or too large"
        }
    }

    //
    // TODO: Find proper way to extend paste plugin.
    // Now pasteimage plagin registers as paste plugin, so init method gets wrong url.
    //
    tinymce.create(
        "tinymce.plugins.PasteImagePlugin:tinymce.plugins.PastePlugin",
        {
            init: function (editor, url)
            {
                this.parent(editor, url);

                url = url.replace(/\bpaste\b/i, "pasteimage");
                tinymce.plugins.PasteImagePluginUtils.ClipboardManager.init(url, editor);

                this.editor = editor;
                this.onPreProcess.addToTop(this._preProcessHandler, this);

                // IE doesn't allow to insert clipboard image.
                // so add ctrl + v handler
                if (tinymce.isIE)
                {
                    this.editor.onKeyDown.addToTop(
                        function(ed, e)
                        {
                            if (((tinymce.isMac ? e.metaKey : e.ctrlKey) && e.keyCode == 86) || (e.shiftKey && e.keyCode == 45))
                            {
                                var clipboardContent = clipboardData.getData("Text") || '';
                                ed.execCommand("mceInsertClipboardContent", false, { content: clipboardContent });
                                e.preventDefault();
                                return tinymce.dom.Event.cancel(e);
                            }
                            return true;
                        });
                }
            },

            getInfo : function()
            {
                return {
                        longname  : 'Paste Image From Clipboard',
                        author    : '',
                        authorurl : '',
                        infourl   : '',
                        version   : tinymce.majorVersion + "." + tinymce.minorVersion
                  };
            },
            
            _preProcessHandler : function(t, o)
            {
                if (tinymce.plugins.PasteImagePluginUtils.ClipboardManager.isFailed() || !this._checkContentForImage(o.content))
                {
                    // nothing to process, or applet not working
                    if (this._checkContentForImage(o.content))
                    {
                        // empty content (pasting of images is not supported).
                        o.content = "";
                    }
                    return true;
                }

                this.editor.setProgressState(1);
                var thisPtr = this;

                tinymce.plugins.PasteImagePluginUtils.ClipboardManager.ready(
                    function ()
                    {
                        thisPtr._processClipboardImage();
                    },

                    function ()
                    {
                        thisPtr.editor.setProgressState(0);
                    }
                );

                o.content = "";
                return true;
            },

            _checkContentForImage : function (clipboardContent)
            {
                // empty clipboard content
                if (!clipboardContent || clipboardContent == "" || clipboardContent.length == 0 || clipboardContent.charCodeAt(0) == 65279)
                {
                    return true;
                }

                // check for img with local path to image
                if (/<img\s*.*\s*src\s*=\s*"\s*file\:\/\//i.test(clipboardContent))
                {
                    return true;
                }

                return false;
            },

            _processClipboardImage : function ()
            {
                var imgInfo = tinymce.plugins.PasteImagePluginUtils.ClipboardManager.getClipboardImage();
                if (imgInfo)
                {
                    imgInfo.onUploadComplete.add(this._onImageUploadComplete, this);
                    imgInfo.onError.add(this._onImageUploadError, this);
                    tinymce.plugins.PasteImagePluginUtils.ClipboardManager.startImageUpload(imgInfo);
                }
                else
                {
                    this.editor.setProgressState(0);
                }
            },

            _onImageUploadComplete : function (newUrl)
            {
                this.editor.setProgressState(0);
                
                var img = "<img src=\"" + newUrl + "\" />";
                this._continueProcess({content : img});
            },

            _onImageUploadError : function (message)
            {
                this.editor.setProgressState(0);
            },

            _continueProcess : function (o)
            {
                var dom = this.editor.dom;
                
                // Create DOM structure
                o.node = dom.create('div', 0, o.content);

                // Execute post process handlers
                this.onPostProcess.dispatch(this, o);

                // Serialize content
                o.content = this.editor.serializer.serialize(o.node, {getInner : 1});

                //  Insert cleaned content. We need to handle insertion of contents containing block elements separately
                if (/<(p|h[1-6]|ul|ol)/.test(o.content))
                {
                    this._insertBlockContent(this.ediotr, dom, o.content);
                }
                else
                {
                    this._insert(o.content);
                }
            },

            _insert : function (content)
            {
                // overrided, to stop insertion, while loading applet.
                if (!content)
                {
                    // do nothing. because currently loading applet, and paste operation should be canceled.
                    return;
                }

                this.parent(content);
            }
        }
    );

    tinymce.createNS("tinymce.plugins.PasteImagePluginUtils");

    tinymce.create(
        "tinymce.plugins.PasteImagePluginUtils.ImageUpload",
        {
            ImageUpload: function (imageInfo)
            {
                this.width = imageInfo.width;
                this.height = imageInfo.height;
                
                this.uploadId = imageInfo.uploadId;

                this.onUploadComplete = new tinymce.util.Dispatcher(this);
                this.onSampleImageComplete = new tinymce.util.Dispatcher(this);
                this.onError = new tinymce.util.Dispatcher(this);
            },

            getId: function ()
            {
                return this.uploadId;
            },

            getWidth: function ()
            {
                return this.width;
            },

            getHeight: function ()
            {
                return this.height;
            }
        });

    tinymce.create(
        "static tinymce.plugins.PasteImagePluginUtils.ClipboardManager",
        {
            init: function (url, editor)
            {
                this.url = url;
                this.editor = editor;
                this.uploads = {};
                this.failed = false;
                this.inited = false;

                this.onReady = new tinymce.util.Dispatcher(this);
                this.onFail  = new tinymce.util.Dispatcher(this);
            },

            applet: function ()
            {
                if (!this._appletInstance)
                {
                    this._appletInstance = this._createApplet();
                }
                return this._appletInstance;
            },

            /**
             * Calls readyHandler when ClipboardManager is Ready,
             * if it fails to initialize it calls failHandler
             *
             * @param function readyHandler
             * @param function failHandler
             */
            ready: function (readyHandler, failHandler)
            {
                var applet = this.applet();
                
                if (this.failed && failHandler)
                {
                    failHandler();
                    return;
                }

                if (this.inited && readyHandler)
                {
                    readyHandler();
                    return;
                }

                this.onFail.add(failHandler);
                this.onReady.add(readyHandler);
                if (applet && applet.checkReadyState)
                {
                    // force checking of ready state
                    applet.checkReadyState();
                }
            },

            isFailed: function ()
            {
                return this.failed;
            },

            clipboardContainsImage: function ()
            {
                var applet = this.applet();
                if (applet)
                {
                    return applet.clipboardContainsImage();
                }
                return false;
            },

            getClipboardImage: function ()
            {
                var applet = this.applet();
                if (applet)
                {
                    var imgInfo = applet.getClipboardImage(false);
                    if (imgInfo)
                    {
                        var upload = new tinymce.plugins.PasteImagePluginUtils.ImageUpload(imgInfo);
                        this.uploads["upl_" + upload.getId()] = upload;
                        return upload;
                    }
                }
                return null;
            },

            startImageUpload: function (uploadInfo)
            {
                var applet = this.applet();
                if (applet && uploadInfo)
                {
                    applet.startImageUpload(uploadInfo.getId());
                }
            },

            _createApplet : function ()
            {
                // check if java supported.
                if (!this._checkForJava())
                {
                    this.failed = true;
                    return null;
                }
                
                var appletContainer = tinymce.DOM.create("div", {id: "appletContainer", style: "position: absolute; left: 0px; top: 0px; z-index: -100"});
                var attribs = this._getAppletAttributes();
                var applet = tinymce.DOM.create("applet", attribs);

                var params = this._getAppletParameters();
                if (!params["uploadUrl"])
                {
                    this.failed = true;
                    return null;
                }

                tinymce.each(params,
                    function (value, key)
                    {
                        applet.appendChild(
                            tinymce.DOM.create("param", {name: key, value: value})
                        );
                    }
                );

                // start loading timeout
                var thisPtr = this;
                this.failTimeOutId = setTimeout(function () { thisPtr._appletFail(); }, APPLET_LOAD_TIMEOUT);

                appletContainer.appendChild(applet);
                document.body.appendChild(appletContainer);

                // force applet to load in opera
                if (tinymce.isOpera)
                {
                    appletContainer.innerHTML = appletContainer.innerHTML;
                    return document.getElementById(attribs["id"]);
                }

                return applet;
            },

            _checkForJava : function ()
            {
                try
                {
                    if (typeof java && typeof java.lang && typeof java.lang.String)
                    {
                        return true;
                    }
                }
                catch (ex)
                {}
                
                return navigator.javaEnabled ? navigator.javaEnabled() : false;
            },

            _getAppletAttributes : function ()
            {
                return {
                  'id'        : APPLET_ID,
                  'code'      : 'ispringcrm.ClipboardManager',
                  'archive'   : this.url + '/ClipboardManager.jar',
                  'width'     : 1,
                  'height'    : 1,
                  'mayscript' : 'mayscript'
                };
            },

            _getAppletParameters : function ()
            {
                return {
                  'listenerObject'        : 'tinymce.plugins.PasteImagePluginUtils.ClipboardManager',

                  'onInit'                : '_appletReady',
                  'onFail'                : '_appletFail',
                  'onImageUploaded'       : '_onUploadComplete',
                  'onAllImagesUploaded'   : '_onAllImagesUploaded',
                  'onImageSampleReady'    : '_onImageSampleReady',

                  'onError'               : '_onError',

                  'sampleQuality'         : this.editor.getParam("pasteimage_sample_quality", DEFAULT_SAMPLE_QUALITY),
                  'imageQuality'          : this.editor.getParam("pasteimage_quality",        DEFAULT_QUALITY),

                  'maxWidth'              : this.editor.getParam("pasteimage_max_width",      DEFAULT_MAX_WIDTH),
                  'maxHeight'             : this.editor.getParam("pasteimage_max_height",     DEFAULT_MAX_HEIGHT),

                  'uploadUrl'             : this.editor.getParam("pasteimage_upload_url",     ""),
                  'uploadFileParamName'   : this.editor.getParam("pasteimage_upload_file_param_name", DEFAULT_FILE_PARAM_NAME)
                };
            },

            // applet events handlers
            _onUploadComplete : function (uploadId, realUrl)
            {
                this._callEventFromParams("pasteimage_on_upload_complete", [uploadId, realUrl]);
                if (this.uploads["upl_" + uploadId])
                {
                    this.uploads["upl_" + uploadId].onUploadComplete.dispatch(realUrl);
                }
            },

            _onAllImagesUploaded : function ()
            {
                this._callEventFromParams("pasteimage_on_all_uploads_complete", []);
            },

            _onImageSampleReady : function (uploadId, dataUrl)
            {
                this._callEventFromParams("pasteimage_on_sample_ready", [uploadId, dataUrl]);
                if (this.uploads["upl_" + uploadId])
                {
                    this.uploads["upl_" + uploadId].onImageSampleReady.dispatch(dataUrl);
                }
            },

            _onError : function (uploadId, message, errorType)
            {
                var errorHandlerResult = { messageShown: false, errorHandled: false, sender: this };

                if (!errorHandlerResult.errorHandled)
                {
                    this._callEventFromParams("pasteimage_on_" + this._getErrorHandlerName(errorType) + "_error", [uploadId, message, errorType, errorHandlerResult]);
                }

                if (!errorHandlerResult.errorHandled)
                {
                    this._callEventFromParams("pasteimage_on_error", [uploadId, message, errorType, errorHandlerResult]);
                }
                
                if (!errorHandlerResult.messageShown)
                {
                    this._defaultShowErrorMessage(message, errorType, errorHandlerResult);
                }

                if (this.uploads["upl_" + uploadId])
                {
                    this.uploads["upl_" + uploadId].onError.dispatch(message, errorType);
                }
            },

            _defaultShowErrorMessage : function (message, errorType, handlingResult)
            {
                var messageInfo = ERROR_MESSAGES[errorType];
                if (messageInfo && messageInfo['paramName'])
                {
                    var messageStr = this.editor.getParam("pageimage_" + messageInfo['paramName'], messageInfo['defaultMessage']);
                    if (messageStr)
                    {
                        alert(this._substitueParams(messageStr, {message: message}));
                        handlingResult.messageShown = true;
                    }
                }
            },

            _substitueParams : function (string, params)
            {
                for (var paramName in params)
                {
                    string = string.replace("{" + paramName + "}", params[paramName]);
                }
                
                return string;
            },

            _getErrorHandlerName : function (errorType)
            {
                return errorType.replace(/([A-Z])/g, "_$1").toLowerCase().replace(/^_/, '');
            },

            _appletReady : function ()
            {
                this.failed = false;
                this.inited = true;
                
                if (this.failTimeOutId)
                {
                    clearTimeout(this.failTimeOutId);
                }
                this.onReady.dispatch(this);
                
                // clear all ready event listeners
                this.onReady = new tinymce.util.Dispatcher(this);
                this.onFail  = null;
            },

            _appletFail : function ()
            {
                this.failed = true;
                if (this.failTimeOutId)
                {
                    clearTimeout(this.failTimeOutId);
                }
                
                if (this.onFail)
                {
                    this.onFail.dispatch(this);
                }
            },
            
            _callEventFromParams : function (paramName, args)
            {
                var callback = this.editor.getParam(paramName);
                if (callback && typeof callback == "function")
                {
                    callback.apply(window, args);
                }
            }
        }
    );

    tinymce.PluginManager.add('paste', tinymce.plugins.PasteImagePlugin);
})();